Immutable Classes in Java
π The Fun Guide to Java Immutability πβ
π₯ What is Immutability?β
Imagine having a magical box π§ββοΈ that, once sealed, never lets you change whatβs inside. Thatβs what an immutable object is in Javaβonce created, it stays the same forever! βοΈ Any modifications? Nope! Youβll have to create a brand-new object instead.
Letβs see an example with the legendary String
class:
String string = "test";
String newString = string.toLowerCase(); // Creates a new String
Here, string
remains untouched, while newString
holds the modified value. Java is all about playing it safe! π
ποΈ Immutability in Collectionsβ
Java gives us three cool ways to create immutable collections:
1οΈβ£ Unmodifiable collections
2οΈβ£ Immutable factory methods (Java 9+)
3οΈβ£ Immutable copies (Java 10+)
Collections.unmodifiableList(recordList); // Unmodifiable list
List.of(new Record(1, "test")); // Factory methods in Java 9
List.copyOf(recordList); // Java 10
π¨ Warning: These are only "shallowly immutable." You canβt add or remove elements, but the existing elements inside can still mutate. π±
Example:
List<Record> list = List.of(new Record(1, "value"));
System.out.println(list); // [Record(id=1, name=value)]
list.get(0).setName("modified-value");
System.out.println(list); // [Record(id=1, name=modified-value)]
To achieve true immutability, only add immutable objects to the collection. This ensures that even if someone gets a reference to an element, they CANβT modify it. π
π¨ How to Create an Immutable Classβ
Java suggests the following to craft a bulletproof immutable class:
β
No setter methods! Setters allow change, which we donβt want. β
β
Make fields final
and private
. Once set, never modified. π
β
Prevent method overriding. Just make the class final
. π
β
Handle mutable fields cautiously! If an object has a list, return a new copy instead of the original.
final class Record {
private final long id;
private final String name;
private final List<String> tokens;
public Record(long id, String name, List<String> tokens) {
this.id = id;
this.name = name;
this.tokens = List.copyOf(tokens);
}
public long getId() { return id; }
public String getName() { return name; }
public List<String> getTokens() { return tokens; }
@Override
public String toString() {
return "Record{" +
"id=" + id +
", name='" + name + '\'' +
", tokens=" + tokens +
'}';
}
}
Letβs try modifying it:
List<String> tokens = new ArrayList<>();
tokens.add("active");
Record record = new Record(1, "value", tokens);
System.out.println(record); // Record{id=1, name='value', tokens=[active]}
record.getTokens().add("new token");
System.out.println(record); // Record{id=1, name='value', tokens=[active]}
BOOM! π₯ The original object remains unchanged!
β‘ Immutability with Java Recordsβ
Java records (introduced in Java 14) remove boilerplate code while making objects immutable. π
record Record(long id, String name, List<String> tokens){
public List<String> tokens() {
return List.copyOf(tokens);
}
}
Testing immutability:
List<String> tokens = new ArrayList<>();
tokens.add("active");
Record record = new Record(1, "value", tokens);
System.out.println(record); // Record{id=1, name='value', tokens=[active]}
record.tokens().add("new token");
System.out.println(record); // Record{id=1, name='value', tokens=[active]}
Records enforce immutability with less code! π
π Immutable Classes in JDKβ
Java already has a ton of built-in immutable classes:
β java.lang.String
β Wrapper classes (Integer
, Long
, Double
, etc.)
β java.math.BigInteger
and BigDecimal
β Unmodifiable collections (Collections.singletonMap()
)
β java.util.UUID
β Java Enums π₯
β Java 8 Date/Time API (LocalDate
, LocalTime
)
β Record types π―
π― Why Should You Care?β
β Predictabilityβ
Immutable objects never change, making debugging much easier! π§
β Thread-Safetyβ
Since nothing changes, no race conditions or synchronization nightmares. Win! π
β No More Copying!β
Immutable objects donβt need a copy constructor or clone(). Java just reuses them. π
β Better Performance πβ
Caching? Check! Memoization? Check! Immutable objects are perfect for optimization.
π¬ Conclusionβ
Immutable classes in Java:
β
Are simple to construct, test, and use
β
Eliminate synchronization issues
β
Make great Map keys and Set elements
β
Ensure βfailure atomicityβ (If an error occurs, objects are never left in a bad state)
Pro Tip: Always aim to make your Java classes immutable. It saves you from a ton of headaches down the road! πβ
π Happy Coding! π